#include "StartTLSReceiver.h"
#include "../../comm/ssl/SSLServer.h"
#include "../../comm/ssl/SSLServerSession.h"
#include "../../comm/ssl/SSLCommon.h"
#include "../../comm/tcp/TCPCommon.h"
#include "../../system/Observers.h"

using namespace kc_sf;

// --------------------------------------------------------- //

StartTLSReceiver::StartTLSReceiver( const Connection &connection )
: CommRequestHandler( connection ),
  r_unsafe_receiver( NULL ),
  r_safe_receiver( NULL ),
  r_safe_session( NULL )
{
}

// --------------------------------------------------------- //

StartTLSReceiver::~StartTLSReceiver()
{
	clear();
}

// --------------------------------------------------------- //

CommRequestHandler * StartTLSReceiver::getInstance( const Connection &connection ) const
{
	return new StartTLSReceiver( connection );
}

// --------------------------------------------------------- //

void StartTLSReceiver::operator ()()
{
	r_client_socket = r_connection.cSocketFd;

	if( r_unsafe_receiver == NULL )
	{
		r_unsafe_receiver = new Receiver( new TCPCommon( r_connection.cSocketFd, MAX_LINE_LENGTH ), r_connection );
		r_logger << info << receiver << cidIn( r_connection.cCid ) << "Nawiązano nieszyfrowaną sesję" << flush;
	}

    if( sendReply( const_cast<const Reply &> ( StandardReplies.at( Ok_Greet ) ) ) != Success ) // przywitaj sie z klientem
    {
        r_logger << error  << receiver << cidIn( r_connection.cCid ) << "Nie udało się przesłać klientowi powitania" << flush;
        return;
    }

    r_state = ConnectedState;

    // odbieraj i przetwarzaj komendy
	while( r_state != InitState )
	{
		CommResult result = Success;
		Command cmd;
		if( ( result = recvCommand( cmd ) ) != Success )
			break;

	    switch( cmd.Type )
	    {
			case Quit:
				processQuit();
				return;

	        case Helo:
	            result = processHelo( cmd );
	            break;

	        case Ehlo:
	            result = processEhlo( cmd );
	            break;

	        case Mail:
	            result = processMailFrom( cmd );
	            break;

	        case Rcpt:
	            result = processRcpt( cmd );
	            break;

	        case Data:
	            result = processData();
	            break;

	        case Noop:
	            result = processNoop();
	            break;

	        case Rset:
	            result = processRset();
	            break;

	        case AuthPlain:
	        	result = processAuthPlain();
	        	break;

	        case AuthLogin:
	        	result = processAuthLogin();
	        	break;

	        case StartTLS:
	        	result = processStartTLS();
	        	break;

	        case Vrfy:
	        case Expn:
	        case Help:
	            result = processUnimplemented( cmd );
	            break;

	        case UndefinedCommand:
	        default:
	            result = processUndefined( cmd );
	            break;
	    }

	    if( result == Failure )
	    {
	    	sendReply( const_cast<const Reply &>( StandardReplies.at( Tmp_Aborted ) ) );
	    	break;
	    }
	    else if( result == Timeout )
	    {
	    	sendReply( const_cast<const Reply &>( StandardReplies.at( Tmp_Timeout ) ) );
	    	break;
	    }
	}

	clear();
}

// --------------------------------------------------------- //

CommResult StartTLSReceiver::processHelo(const Command & cmd)
{
	return r_safe_receiver == NULL ? r_unsafe_receiver->processHelo( cmd )
		: r_safe_receiver->processHelo( cmd );
}

// --------------------------------------------------------- //

CommResult StartTLSReceiver::processEhlo(const Command & cmd)
{
	return r_safe_receiver == NULL ? r_unsafe_receiver->processEhlo( cmd )
		: r_safe_receiver->processEhlo( cmd );
}

// --------------------------------------------------------- //

CommResult StartTLSReceiver::processAuthPlain()
{
	return r_safe_receiver == NULL ? r_unsafe_receiver->processAuthPlain()
		: r_safe_receiver->processAuthPlain();
}

// --------------------------------------------------------- //

CommResult StartTLSReceiver::processAuthLogin()
{
	return r_safe_receiver == NULL ? r_unsafe_receiver->processAuthLogin()
		: r_safe_receiver->processAuthLogin();
}

// --------------------------------------------------------- //

CommResult StartTLSReceiver::processStartTLS()
{
	if( r_safe_receiver )
	{
		Reply reply = StandardReplies.at( Tmp_StartTLSRepeat );
		return r_safe_receiver->sendReply( reply );
	}

	Reply reply = StandardReplies.at( Ok_GoAhead );
	CommResult result = r_unsafe_receiver->sendReply( reply );
	if( result != Success )
		return result;

	r_safe_session = new SSLServerSession( r_client_socket );
	SSL *ssl = r_safe_session->acceptClient ();
	r_safe_receiver = new SSLReceiver( new SSLCommon( r_connection.cSocketFd, ssl, MAX_LINE_LENGTH ), r_connection );
	r_logger << info << receiver << cidIn( r_connection.cCid ) << "Nawiązano szyfrowaną sesję SSL" << flush;

	return Success;
}

// --------------------------------------------------------- //

CommResult StartTLSReceiver::processMailFrom(const Command & cmd)
{
	return r_safe_receiver == NULL ? r_unsafe_receiver->processMailFrom( cmd )
		: r_safe_receiver->processMailFrom( cmd );
}

// --------------------------------------------------------- //

CommResult StartTLSReceiver::processRcpt(const Command & cmd)
{
	return r_safe_receiver == NULL ? r_unsafe_receiver->processRcpt( cmd )
		: r_safe_receiver->processRcpt( cmd );
}

// --------------------------------------------------------- //

CommResult StartTLSReceiver::processData()
{
	return r_safe_receiver == NULL ? r_unsafe_receiver->processData()
		: r_safe_receiver->processData();
}

// --------------------------------------------------------- //

CommResult StartTLSReceiver::processNoop()
{
	return r_safe_receiver == NULL ? r_unsafe_receiver->processNoop()
		: r_safe_receiver->processNoop();
}

// --------------------------------------------------------- //

CommResult StartTLSReceiver::processQuit()
{
	r_state = InitState;
	return r_safe_receiver == NULL ? r_unsafe_receiver->processQuit()
			: r_safe_receiver->processQuit();
}

// --------------------------------------------------------- //

CommResult StartTLSReceiver::processRset()
{
	return r_safe_receiver == NULL ? r_unsafe_receiver->processRset()
		: r_safe_receiver->processRset();
}

// --------------------------------------------------------- //

CommResult StartTLSReceiver::processUnimplemented(const Command & cmd)
{
	return r_safe_receiver == NULL ? r_unsafe_receiver->processUnimplemented( cmd )
		: r_safe_receiver->processUnimplemented( cmd );
}

// --------------------------------------------------------- //

CommResult StartTLSReceiver::processUndefined(const Command & cmd)
{
	return r_safe_receiver == NULL ? r_unsafe_receiver->processUndefined( cmd )
		: r_safe_receiver->processUndefined( cmd );
}

// --------------------------------------------------------- //

CommResult StartTLSReceiver::recvCommand( Command &cmd, int timeout_s )
{
	return r_safe_receiver == NULL ? r_unsafe_receiver->recvCommand( cmd, timeout_s )
			: r_safe_receiver->recvCommand( cmd, timeout_s );
}

// ---------------------------------------------------------------- //

CommResult StartTLSReceiver::recvLine( std::string &to, int timeout_s )
{
	return r_safe_receiver == NULL ? r_unsafe_receiver->recvLine( to, timeout_s )
			: r_safe_receiver->recvLine( to, timeout_s );
}

// ---------------------------------------------------------------- //

CommResult StartTLSReceiver::sendReply( const Reply &reply, int timeout_s )
{
	return r_safe_receiver == NULL ? r_unsafe_receiver->sendReply( reply, timeout_s )
			: r_safe_receiver->sendReply( reply, timeout_s );
}

// ---------------------------------------------------------------- //

void StartTLSReceiver::clear()
{
	if( r_unsafe_receiver )
	{
		delete r_unsafe_receiver;
		r_unsafe_receiver = NULL;
	}

	if( r_safe_receiver )
	{
		delete r_safe_receiver;
		r_safe_receiver = NULL;
	}

	if( r_safe_session )
	{
		delete r_safe_session;
		r_safe_session = NULL;
	}
}
